
package com.bangcommunity.bbframe.dynconfig;

import com.bangcommunity.bbframe.common.base.MainConfigLoader;
import com.bangcommunity.bbframe.dynconfig.domain.Configuration;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

/**
 * DynConfigProcessor 工厂类
 *
 * @author tanghc
 */
public class DynConfigRegister {

    static Logger logger = LoggerFactory.getLogger(DynConfigRegister.class);

    private static ReentrantLock lock = new ReentrantLock();

    private static class DynConfigRegisterHolder {

        private static DynConfigRegister instance = new DynConfigRegister();
    }

    private DynConfigRegister() {
    }

    private static ConcurrentMap<String, IDynConfigProcessor> dynConfigProcessorMap = Maps.newConcurrentMap();

    /**
     * 获取DynConfigProcessor实例(单例)
     *
     * @return
     */
    public static DynConfigRegister getInstance() {

        return DynConfigRegisterHolder.instance;
    }

    /**
     * 对于同一个坐标配置信息只能创建一次
     *
     * @return
     */
    public IDynConfigProcessor buildOnce(final String group, final String dataId,
            IDynConfigProcessor dynConfigProcessor) {

        return buildOnce(MainConfigLoader.getInstance().getProductCode(), MainConfigLoader.getInstance().getAppCode(),
                group, dataId, dynConfigProcessor);
    }

    public IDynConfigProcessor build(final String group, final String dataId, IDynConfigProcessor dynConfigProcessor) {

        return build(MainConfigLoader.getInstance().getProductCode(), MainConfigLoader.getInstance().getAppCode(),
                group, dataId, dynConfigProcessor);
    }

    public IDynConfigProcessor buildOnce(final String productCode, final String appName, final String group,
            final String dataId, final IDynConfigProcessor dynConfigProcessor) {

        DynConfigClient dynConfigClient = DynConfigClientFactory.getClient();
        return build(dynConfigClient, productCode, appName, group, dataId, dynConfigProcessor, true);
    }

    public IDynConfigProcessor build(final String productCode, final String appName, final String group,
            final String dataId, final IDynConfigProcessor dynConfigProcessor) {

        DynConfigClient dynConfigClient = DynConfigClientFactory.getClient();
        return build(dynConfigClient, productCode, appName, group, dataId, dynConfigProcessor, false);
    }

    public IDynConfigProcessor build(final DynConfigClient dynConfigClient, final String productCode,
            final String appName, final String group, final String dataId, final IDynConfigProcessor dynConfigProcessor,
            final boolean isOnce) {

        lock.lock();
        String key = new StringBuilder().append(productCode).append(":").append(appName).append(":").append(group)
                .append(":").append(dataId).append(":").toString();
        try {
            // 校验是否是一次创建
            if (isOnce && null != dynConfigProcessorMap.get(key)) {
                throw new RuntimeException("IDynConfigProcessor can only build once");
            }
            // 获取参数
            String config = dynConfigClient.getConfig(productCode, appName, group, dataId);
            if (null != dynConfigProcessor) {
                dynConfigProcessor.procesConfig(config);
            }
            // 注册动态修改参数事件
            dynConfigClient.registerListeners(productCode, appName, group, dataId, new IChangeListener() {

                @Override
                public Executor getExecutor() {

                    return Executors.newSingleThreadExecutor();
                }

                @Override
                public void receiveConfigInfo(final Configuration configuration) {

                    getExecutor().execute(new Runnable() {

                        @Override
                        public void run() {

                            String config = configuration.getConfig();
                            if (null != dynConfigProcessor) {
                                dynConfigProcessor.procesConfig(config);
                            }
                        }
                    });
                }
            });
        } catch (Exception e) {
            logger.error("build IDynConfigProcessor error: product={},app={},group={},dataid={}", productCode, appName,
                    group, dataId, e);
        } finally {
            dynConfigProcessorMap.put(key, dynConfigProcessor);
            lock.unlock();
        }

        return dynConfigProcessor;
    }

}
